#include "GroupTable.h"

#include "EventFileDriver.h"
#include "EventHash.h"
#include "EventList.h"
#include "EventNode.h"
#include "GroupItem.h"

#include <libintl.h>
#include <stdio.h>
#include <stdlib.h>
#define _(String) gettext(String)

struct _GroupTable
{
	EventFileDriver parent_instance;
	EventHash      *g_table;
};

static int
group_table_parser(EventFileDriver *driver, const epointer data, int length)
{
	EventList *list;
	EventNode *node;
	GroupItem *item;
	GMember	  *g_member;

	return_val_if_fail(driver != NULL, group_table_parser, 1);

	list = event_list_new_with_raw(data);
	if (!list)
		return 1;

	node = EVENT_NODE(list);
	if (node->data == NULL) {
		event_object_unref(list);
		return 1;
	}

	item		  = group_item_new();
	item->group_id	  = (euint32)(euint64)node->data;
	node		  = node->next;
	item->user_id	  = (euint32)(euint64)node->data;
	node		  = node->next;
	item->create_date = (time_t)node->data;
	node		  = node->next;
	item->modify_date = (time_t)node->data;
	node		  = node->next;
	group_item_set_group_name(item, EVENT_STRING(node->data));
	node		  = node->next;
	item->g_can_write = (bool)(euint64)node->data;
	node		  = node->next;
	item->can_join	  = (bool)(euint64)node->data;
	node		  = node->next;
	/* 用户创建以后会默认加入系统消息事件组，无需担心数据为 NULL */
	node = EVENT_NODE(node->data);
	while (node) {
		g_member = (GMember *)&node->data;
		group_item_set_member(item, g_member->user_id,
				      g_member->can_write);
		node = node->next;
	}
	event_hash_set(GROUP_TABLE(driver)->g_table,
		       EPTR((euint64)item->group_id), item, event_object_unref);
	event_object_unref(list);
	return 0;
}

static EventFileDriverPack *
group_table_writer(EventFileDriver *driver)
{
	GroupTable	 *self = GROUP_TABLE(driver);
	EventList	 *list, *m_list;
	EventRaw	 *raw;
	HashItem_p	  h_item;
	GroupItem	 *g_item;
	volatile GMember *gm;

	return_val_if_fail(driver != NULL, group_table_writer, NULL);

	h_item = event_hash_iter_get(self->g_table);
	if (!h_item)
		return NULL;
	g_item = event_hash_item_get_value(h_item);

	group_item_iter_member_reset(g_item);
	m_list = event_list_new();
	gm     = group_item_iter_get_with_member(g_item);
	while (gm) {
		event_list_add_data(m_list, EPTR(*((euint64 *)gm)),
				    EVENT_TYPE_UINT64, NULL);
		gm = group_item_iter_get_with_member(g_item);
	}

	list = event_list_new();
	event_list_add_data(list, EPTR((euint64)g_item->group_id),
			    EVENT_TYPE_UINT32, NULL);
	event_list_add_data(list, EPTR((euint64)g_item->user_id),
			    EVENT_TYPE_UINT32, NULL);
	event_list_add_data(list, EPTR(g_item->create_date), EVENT_TYPE_INT64,
			    NULL);
	event_list_add_data(list, EPTR(g_item->modify_date), EVENT_TYPE_INT64,
			    NULL);
	event_list_add_data(list,
			    EO_REF(group_item_get_group_name(g_item), void),
			    EVENT_TYPE_STRING, event_object_unref);
	event_list_add_data(list, EPTR((euint64)g_item->g_can_write),
			    EVENT_TYPE_UINT8, NULL);
	event_list_add_data(list, EPTR((euint64)g_item->can_join),
			    EVENT_TYPE_UINT8, NULL);
	event_list_add_data(list, m_list, EVENT_TYPE_LIST, event_object_unref);
	raw = event_list_export_raw(list);
	event_object_unref(list);
	return event_file_driver_pack_new_from_raw(raw);
}

static int
group_table_prepare_data(EventFileDriver *driver)
{
	GroupTable *self = GROUP_TABLE(driver);
	return_val_if_fail(driver != NULL, group_table_prepare_data,
			   EVENT_FILE_DRIVER_PREPARE_DATA_ERROR);
	event_hash_iter_reset(GROUP_TABLE(driver)->g_table);
	event_file_driver_flush_items_num(
		driver, (eint32)event_hash_get_used_size(self->g_table));
	return EVENT_FILE_DRIVER_SUCCESS;
}

static void
group_table_finalize(GroupTable *self)
{
	event_file_driver_finalize(EVENT_FILE_DRIVER(self));
	event_object_unref(self->g_table);
}

GroupTable *
group_table_new(void)
{
	GroupTable *object = MALLOC_NEW(GroupTable);

	event_file_driver_init(EVENT_FILE_DRIVER(object));
	event_object_set_finalize(object, FINALIZE(group_table_finalize));

	event_file_driver_set_parser(EVENT_FILE_DRIVER(object),
				     group_table_parser);
	event_file_driver_set_writer(EVENT_FILE_DRIVER(object),
				     group_table_writer,
				     group_table_prepare_data);
	event_file_driver_set_file_not_exist_throw(EVENT_FILE_DRIVER(object),
						   false);

	object->g_table = event_hash_new(EVENT_HASH_MODEL_NUMBER);

	return object;
}

GroupItem *
group_table_get_g_item(GroupTable *self, euint32 group_id)
{
	return_val_if_fail(self != NULL, group_table_get_g_item, NULL);
	return (GroupItem *)event_hash_get(self->g_table,
					   EPTR((euint64)group_id));
}

void
group_table_make_sure_default_exist(GroupTable *self)
{
	return_if_fail(self != NULL, group_table_make_sure_default_exist);
	GroupItem *item = event_hash_get(self->g_table, EPTR(1L));
	if (item)
		return;
	item			= group_item_new();
	item->can_join		= false;
	item->create_date	= 0;
	item->g_can_write	= false;
	item->group_id		= 1;
	item->modify_date	= 0;
	item->user_id		= 0;
	EventString *group_name = event_string_new(_("Defalt System Group"));
	group_item_set_group_name(item, group_name);
	event_object_unref(group_name);
	/* GroupItem
         * 由哈希表保存，由于默认创建新哈希表对象，此处不用添加任何数据 */
	event_hash_set(self->g_table, EPTR(1L), item, event_object_unref);
}

int
group_table_add_member(GroupTable *self, euint32 group_id, euint32 user_id,
		       bool can_write)
{
	return_val_if_fail(self != NULL, group_table_add_member, 2);
	GroupItem *item = (GroupItem *)event_hash_get(self->g_table,
						      EPTR((euint64)group_id));
	if (!item)
		return 1;
	group_item_set_member(item, user_id, false);
	return 0;
}

static euint32
random_group_id(euint32 user_id, time_t now_time)
{
	static euint32 already = 0;
	if (user_id == 0) {
		already = 0;
		return 0;
	}
	if (already == 0)
		already = user_id;
	else
		already += (euint32)(now_time % 8979531);
	return already % 8999509;
}

euint32
group_table_add_item(GroupTable *self, euint32 creator_uid,
		     EventString *group_name, bool g_can_write, bool can_join)
{
	GroupItem *g_item;
	euint32	   group_id;
	time_t	   now_time;
	return_val_if_fail(self != NULL && group_name != NULL,
			   group_table_add_item, 0);
	now_time = time(NULL);
	do {
		group_id = random_group_id(creator_uid, now_time) + 1000000;
		g_item	 = group_table_get_g_item(self, group_id);
	} while (g_item);
	random_group_id(0, 0);

	g_item = group_item_new();
	//
	g_item->can_join    = can_join;
	g_item->create_date = now_time;
	g_item->g_can_write = g_can_write;
	g_item->group_id    = group_id;
	g_item->modify_date = now_time;
	g_item->user_id	    = creator_uid;
	group_item_set_group_name(g_item, group_name);
	/* 将创建者本身加入事件组中 */
	group_item_set_member(g_item, creator_uid, true);
	event_hash_set(self->g_table, EPTR((euint64)group_id), g_item,
		       event_object_unref);
	return group_id;
}

void
group_table_remove_group(GroupTable *self, euint32 group_id)
{
	return_if_fail(self != NULL, group_table_remove_group);
	event_hash_remove(self->g_table, EPTR((euint64)group_id));
}

void
__group_table_add(GroupTable *self, GroupItem *item)
{
	return_if_fail(self != NULL && item != NULL, __group_table_add);
	event_hash_set(self->g_table, EPTR((euint64)item->group_id), item,
		       event_object_unref);
}

void
__group_table_print(GroupTable *self, euint32 group_id)
{
	GroupItem *item;
	GMember	  *gm;
	return_if_fail(self != NULL, __group_table_print);

	item = (GroupItem *)event_hash_get(self->g_table,
					   EPTR((euint64)group_id));
	if (!item) {
		printf("无该事件组。\n");
		return;
	}
	printf("==========================\n");
	printf("事件组 '%s' 信息：\n", ES_GET(group_item_get_group_name(item)));
	printf("  GroupID:\t%07u\n", item->group_id);
	printf("  创建者 ID:\t%07u\n", item->user_id);
	printf("  创建日期:\t%ld\n", item->create_date);
	printf("  最后修改:\t%ld\n", item->modify_date);
	printf("  是否可写？<%s>\n", item->g_can_write ? "是" : "否");
	printf("  是否允许加入？<%s>\n", item->can_join ? "是" : " 否");
	printf("加入该事件组的成员 ID:\n");

	group_item_iter_member_reset(item);
	gm = group_item_iter_get_with_member(item);
	while (gm) {
		printf("  %07u: %s\n", gm->user_id,
		       gm->can_write ? "可写" : "只读");
		gm = group_item_iter_get_with_member(item);
	}
}
